﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Web;

namespace _5E.WebInfrastructure.UserControl
{
    public class UserControlManager
    {
        private static Dictionary<Type, Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>> s_MetadataCache =
            new Dictionary<Type, Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>>();
        private static Dictionary<PropertyInfo, object> s_DefaultValue = new Dictionary<PropertyInfo, object>();
        private static object s_Mutex = new object();

        public static void SetPropertyValues(System.Web.UI.UserControl control, HttpRequest request)
        {
            var type = control.GetType();
            var metadata = GetMetadata(type);
            foreach (var property in metadata.Keys)
            {
                var value = GetValue(metadata[property], request) ?? GetDefaultValue(property);
                if (value != null)
                    property.SetValue(control, Convert.ChangeType(value, property.PropertyType), null);
            }
        }

        private static Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> GetMetadata(Type type)
        {
            Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> metadata;
            if (!s_MetadataCache.TryGetValue(type, out metadata))
            {
                lock (s_Mutex)
                {
                    if (!s_MetadataCache.TryGetValue(type, out metadata))
                        metadata = LoadMetadata(type);
                }
            }
            return metadata;
        }

        private static Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> LoadMetadata(Type type)
        {
            var metadata = new Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>();
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
            foreach (var property in properties)
            {
                var attributes = property.GetCustomAttributes(typeof(UserControlRenderingPropertyAttribute), true);
                if (0 < attributes.Length)
                {
                    metadata.Add(property, new List<UserControlRenderingPropertyAttribute>(
                        attributes.Cast<UserControlRenderingPropertyAttribute>()));
                }
            }
            return metadata;
        }

        private static object GetValue(IEnumerable<UserControlRenderingPropertyAttribute> attributes, HttpRequest request)
        {
            foreach (var attribute in attributes)
            {
                var value = request.Params[attribute.Key];
                if (null != value)
                    return value;
            }
            return null;
        }

        private static object GetDefaultValue(PropertyInfo property)
        {
            object value;
            if (!s_DefaultValue.TryGetValue(property, out value))
            {
                lock (s_Mutex)
                {
                    if (!s_DefaultValue.TryGetValue(property, out value))
                    {
                        var attributes = property.GetCustomAttributes(typeof(DefaultValueAttribute), true);
                        value = 0 < attributes.Length ? (attributes[0] as DefaultValueAttribute).Value : null;
                        s_DefaultValue.Add(property, value);
                    }
                }
            }
            return value;
        }
    }
}
